package com.ojoin.trade.common.web.quartz;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

import com.ojoin.trade.common.utils.LogUtils;
import com.ojoin.trade.common.utils.SpringUtils;
import com.ojoin.trade.common.web.task.BaseQuartzJob;

@Component
public class QuartzTaskService {    
    
	private final Logger logger = LoggerFactory.getLogger(this.getClass());    
	    
	@Autowired     
	private SchedulerFactoryBean schedulerFactoryBean;      
	  
	public Map<String, String> status() {
		Map<String, String> ret = new HashMap();
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		try {
			ret.put("isStart", Boolean.toString(scheduler.isStarted()));
			ret.put("isInStandbyMode", Boolean.toString(scheduler.isInStandbyMode()));
            ret.put("metaSummary",scheduler.getMetaData().getSummary());
		} catch (Exception e) {
			ret.put("error", e.getMessage());
		}
		return ret;
	}
	
	/**
	 * @param rescheduleJob
	 * @throws SchedulerException
	 */
	public void init(boolean rescheduleJob) throws SchedulerException {		     
		// 可执行的任务列表        
		List<QuartzBean> taskList = getTaskList();     
		if(rescheduleJob){
			List<QuartzBean> allJobs=getAllJobs();
			LogUtils.info(logger,"删除所有任务 %s ......",allJobs.size());  
			for (QuartzBean job : allJobs) {     
				deleteJob(job);     
			}   
		}
		
		LogUtils.info(logger,"初始化加载定时任务......");  
		for (QuartzBean job : taskList) {     
			addJob(job);     
		}     
	}	  
	/**    
	 * 获取单个任务    
	 * @param jobName    
	 * @param jobGroup    
	 * @return    
	 * @throws SchedulerException    
	 */    
	public QuartzBean getJob(String jobName,String jobGroup) throws SchedulerException{    
		QuartzBean job = null;    
		Scheduler scheduler = schedulerFactoryBean.getScheduler();    
		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);    
		CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);      
		if (null != trigger) {    
			job = new QuartzBean();    
	        job.setJobName(jobName);    
	        job.setJobGroup(jobGroup);    
	        job.setDescription("触发器:" + trigger.getKey());    
	        job.setNextTime(trigger.getNextFireTime()); //下次触发时间    
	        job.setPreviousTime(trigger.getPreviousFireTime());//上次触发时间    
	        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());    
	        job.setJobStatus(triggerState.name());    
	        if (trigger instanceof CronTrigger) {    
	            CronTrigger cronTrigger = (CronTrigger) trigger;    
	            String cronExpression = cronTrigger.getCronExpression();    
	            job.setCronExpression(cronExpression);    
	        }    
		}		    
        return job;    
	}    
	/**    
	 * 获取所有任务    
	 * @return    
	 * @throws SchedulerException    
	 */    
	public List<QuartzBean> getAllJobs() throws SchedulerException{    
		Scheduler scheduler = schedulerFactoryBean.getScheduler();      
		GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();    
		Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);    
		List<QuartzBean> jobList = new ArrayList<QuartzBean>();    
		for (JobKey jobKey : jobKeys) {    
		    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);    
		    for (Trigger trigger : triggers) {    
		        QuartzBean job = new QuartzBean();    
		        job.setJobName(jobKey.getName());    
		        job.setJobGroup(jobKey.getGroup());    
		        job.setDescription("触发器:" + trigger.getKey());    
		            
		        job.setNextTime(trigger.getNextFireTime()); //下次触发时间		       
		        job.setPreviousTime(trigger.getPreviousFireTime());//上次触发时间    
		        //触发器当前状态    
		        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());    
		        job.setJobStatus(triggerState.name());    
		        //    
		        if (trigger instanceof CronTrigger) {    
		            CronTrigger cronTrigger = (CronTrigger) trigger;    
		            String cronExpression = cronTrigger.getCronExpression();    
		            job.setCronExpression(cronExpression);    
		        }    
		        jobList.add(job);    
		    }    
		}    
		return jobList;    
	}    
	    
	/**    
	 * 所有正在运行的job    
	 *     
	 * @return    
	 * @throws SchedulerException    
	 */    
	public List<QuartzBean> getRunningJob() throws SchedulerException {    
		Scheduler scheduler = schedulerFactoryBean.getScheduler();    
		List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();    
		List<QuartzBean> jobList = new ArrayList<QuartzBean>(executingJobs.size());    
		for (JobExecutionContext executingJob : executingJobs) {    
			QuartzBean job = new QuartzBean();    
			JobDetail jobDetail = executingJob.getJobDetail();    
			JobKey jobKey = jobDetail.getKey();    
			Trigger trigger = executingJob.getTrigger();    
			job.setJobName(jobKey.getName());    
			job.setJobGroup(jobKey.getGroup());    
			job.setDescription("触发器:" + trigger.getKey());    
			Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());    
			job.setJobStatus(triggerState.name());    
			if (trigger instanceof CronTrigger) {    
				CronTrigger cronTrigger = (CronTrigger) trigger;    
				String cronExpression = cronTrigger.getCronExpression();    
				job.setCronExpression(cronExpression);    
			}    
			jobList.add(job);    
		}    
		return jobList;    
	}    
	    
	     
	 
	/**    
	 * 添加任务    
	 *     
	 * @param job
	 * @throws SchedulerException    
	 */    
	public boolean addJob(QuartzBean job) throws SchedulerException {    
		if (job == null || !QuartzBean.STATUS_RUNNING.equals(job.getJobStatus())) {    
			return false;    
		}    
		if(!TaskUtils.isValidExpression(job.getCronExpression())){    
			logger.error("时间表达式错误（"+job.getJobName()+","+job.getJobGroup()+"）,"+job.getCronExpression());    
			return false;    
		}else{    
			Scheduler scheduler = schedulerFactoryBean.getScheduler();    
			// 任务名称和任务组设置规则：    // 名称：task_1 ..    // 组 ：group_1 ..        
			TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());    
			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);        
			// 不存在，创建一个       
			if (null == trigger) {
				//是否允许并发执行    
				Class<? extends Job> clazz = QuartzBean.CONCURRENT_IS.equals(job.isConcurrent()) ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;    
				JobDetail jobDetail = JobBuilder.newJob(clazz).storeDurably(true).requestRecovery(true).withDescription(job.getDescription()).withIdentity(job.getJobName(), job.getJobGroup()).build();     
				jobDetail.getJobDataMap().put("scheduleJob", job);     
				// 表达式调度构建器         
				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());    
				// 按新的表达式构建一个新的trigger         
				trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
				scheduler.scheduleJob(jobDetail, trigger);       
			} else {     // trigger已存在，则更新相应的定时设置         
				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());         
				// 按新的cronExpression表达式重新构建trigger         
				trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();        
				
				/*// 按新的trigger重新设置job执行(ignore paused)
				Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());      
				if(rescheduleJob){
					 scheduler.rescheduleJob(triggerKey, trigger);       
					 LogUtils.info(logger, String.format("任务<%s>重新配置!",job.getJobName()));
				}*/
			}			      
		}    
		return true;    
	}
	/**    
	 * 暂停任务    
	 * @param scheduleJob    
	 * @return    
	 */    
	public boolean pauseJob(QuartzBean scheduleJob){    
		Scheduler scheduler = schedulerFactoryBean.getScheduler();    
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());    
		try {    
			scheduler.pauseJob(jobKey);    
			return true;    
		} catch (SchedulerException e) {			    
		}    
		return false;    
	}    
	    
	/**    
	 * 恢复任务    
	 * @param scheduleJob    
	 * @return    
	 */    
	public boolean resumeJob(QuartzBean scheduleJob){    
		Scheduler scheduler = schedulerFactoryBean.getScheduler();    
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());    
		try {    
			scheduler.resumeJob(jobKey);    
			return true;    
		} catch (SchedulerException e) {			    
		}    
		return false;    
	}    
	    
	/**    
	 * 删除任务    
	 */    
	public boolean deleteJob(QuartzBean scheduleJob){    
		Scheduler scheduler = schedulerFactoryBean.getScheduler(); 
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());    
		try{    
			scheduler.deleteJob(jobKey);    
			return true;    
		} catch (SchedulerException e) {			    
		}    
		return false;    
	}    
	    
	/**    
	 * 立即执行一个任务    
	 * @param scheduleJob    
	 * @throws SchedulerException    
	 */    
	public void runJob(QuartzBean scheduleJob) throws SchedulerException{    
		Scheduler scheduler = schedulerFactoryBean.getScheduler();    
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());    
		scheduler.triggerJob(jobKey);    
	}    
	    
	/**    
	 * 更新任务时间表达式    
	 * @param scheduleJob    
	 * @throws SchedulerException    
	 */    
	public void updateCronExpression(QuartzBean scheduleJob) throws SchedulerException{    
		Scheduler scheduler = schedulerFactoryBean.getScheduler();    
		TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());    
		//获取trigger，即在spring配置文件中定义的 bean id="myTrigger"    
		CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);    
		//表达式调度构建器    
		CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());    
		//按新的cronExpression表达式重新构建trigger    
		trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();    
		//按新的trigger重新设置job执行    
		scheduler.rescheduleJob(triggerKey, trigger);    
	}
	
	public List<QuartzBean> getTaskList() {
		List<QuartzBean> ret=new ArrayList<QuartzBean>();
		
		Map<String, BaseQuartzJob> jobs=SpringUtils.getAbstractType(BaseQuartzJob.class);
		for(Entry<String, BaseQuartzJob> entry:jobs.entrySet()){
			BaseQuartzJob qjob=entry.getValue();
			Class<? extends BaseQuartzJob> aClass = qjob.getClass();
			Method[] methods = aClass.getMethods();
			for (Method method : methods) {
				CronExpression annotation = method.getAnnotation(CronExpression.class);
				if(null==annotation){
					continue;
				}
				boolean isValidCronExp = org.quartz.CronExpression.isValidExpression(annotation.cron());
				if (!isValidCronExp) {
					LogUtils.info(logger, "%s - %s  cron 无效",aClass.getSimpleName(),method.getName());
					continue;
				}

				QuartzBean qb=new QuartzBean();
				qb.setSpringId(entry.getKey());
				qb.setCronExpression(annotation.cron());
				qb.setDescription(annotation.desc());
                qb.setIsConcurrent(annotation.isConcurrent()? QuartzBean.CONCURRENT_IS: QuartzBean.CONCURRENT_NOT);
				qb.setJobStatus(QuartzBean.STATUS_RUNNING);
				qb.setJobClass(aClass.getName());
				qb.setMethodName(method.getName());
				
				qb.setJobName(aClass.getSimpleName()+"-"+method.getName());
				qb.setJobGroup(qjob.getTaskGroupName().getName());

				ret.add(qb);
			}
		}
		return ret;
	}
}	 
	 
	    
	 